/** 	@file ComparerIx.cpp
 *	@brief Comparative functions. */
#include "ComparerIx.h"
#include "Pow.h"
#include "Mul.h"
#include "Add.h"
#include "Fx.h"
#include "Sym.h"
#include "Number.h"
#include "operators.h"
#include "Substitution.h"
namespace sympycpp {
bool ComparerIx::textCompare(const Ex * L, const Ex * R) {
	return L->str() == R->str();
}
bool ComparerIx::unsignedTextCompare(const Ex * L, const Ex * R) {
	return L->unsigned_str() == R->unsigned_str();
}
compatibility ComparerIx::compatibilityForAddition(const Ex * L, const Ex * R) {
	if (L->isNum() && R->isNum()) {
		return NUMBER_NUMBER;	// number + number
	}
	if (L->isMultiple()) {
		const_iterator itL = ((Mul *)L)->args_.begin();
		++itL;
		compatibility partialCompatibility = compatibilityForAddition(*itL, R);
		if (partialCompatibility == PLAIN_PLAIN) {
			return MULTIPLE_PLAIN; // number*A + A
		}
		if (partialCompatibility == PLAIN_MULTIPLE) {
			return MULTIPLE_MULTIPLE; // number*A + number*A
		}
		return INCOMPATIBLE;// A + B
	}
	if (R->isMultiple()) {
		const_iterator itR = ((Mul *)R)->args_.begin();
		++itR;
		compatibility partialCompatibility = compatibilityForAddition(L, *itR);
		if (partialCompatibility == PLAIN_PLAIN) {
			return PLAIN_MULTIPLE;// A + number*A
		}
		return INCOMPATIBLE; // A + B
	}
	if (unsignedTextCompare(L,R)) {
		return PLAIN_PLAIN;// A + A
	}
	return INCOMPATIBLE;// A + B
}
compatibility ComparerIx::compatibilityForMultiplication(const Ex * L, const Ex * R) {
	if ((R->isNum()) && (L->isNum())) {
		return NUMBER_NUMBER; //Number Number
	}
	if (L->isPow()) {
		if (R->isPow()) {
			if (unsignedTextCompare(((Pow*)L)->base_, ((Pow*)R)->base_)) {
				return BASE_BASE; //X^(..) X^(...)
			}
		}
		else {
			if (unsignedTextCompare(((Pow*)L)->base_, R)) {
				return BASE_PLAIN; //X^(..) X
			}
		}
	}
	else {
		if (R->isPow()) {
			if (unsignedTextCompare(L, ((Pow*)R)->base_)) {
				return PLAIN_BASE; //X X^(..) 
			}
	
		}
		else {
			if (unsignedTextCompare(L, R)) {
				return PLAIN_PLAIN;//A A
			}
		}
	}
	return INCOMPATIBLE;
}

bool ComparerIx::addLessThan(const Ex * L, const Ex * R, const bool compareSignSym) {
	if (L->isMultiple()  && R->isMultiple()) {	// number*A ? number*B		 
		const_iterator itL = ((Mul *)L)->args_.begin();
		const_iterator itR = ((Mul *)R)->args_.begin();
		++itL;
		++itR;
		return auxLessThan(*itL, *itR, compareSignSym);// compare A B
	}
	if (L->isMultiple()) {			 //number*A ? B
		const_iterator itL = ((Mul *)L)->args_.begin();
		++itL;
		return auxLessThan(*itL, R, compareSignSym);//compare A B
	}

	if (R->isMultiple()) {			// the same trick as above 
		const_iterator itR = ((Mul *)R)->args_.begin();
		++itR;
		return auxLessThan(L, *itR,compareSignSym);
	}
	return auxLessThan(L, R, compareSignSym);
}
bool ComparerIx::mulLessThan(const Ex * L, const Ex * R, const bool compareSignSym) {
	if (L->isPow() && R->isPow()) {// X^... ? Y^... => compare X Y
		return auxLessThan(((Pow *)L)->base_, ((Pow *)R)->base_, compareSignSym);
	}
	if (L->isPow()) {//X^... Y => compare X Y
		return auxLessThan(((Pow *)L)->base_, R, compareSignSym);
	}
	if (R->isPow()) {// X Y^... => compare X Y
		return auxLessThan(L, ((Pow *)R)->base_, compareSignSym);
	}
	return auxLessThan(L, R, compareSignSym);
}
bool ComparerIx::auxLessThan(const Ex * L, const Ex * R, const bool compareSignSym) {
	size_t rl = L->rsize();
	size_t rr = R->rsize();
	if (rl != rr) {
		return rl < rr;
	}
	if (L->type() != R->type()) {				 
		return L->type() < R->type();
	}
	return auxCompareSubExpresions(L, R, compareSignSym);

}
bool ComparerIx::auxCompareSubExpresions(const Ex * L, const Ex * R, const bool compareSignSym) {
	switch (L->type()) {
		case SYM : {
			if (compareSignSym) {
				if ((L->unsigned_str() == R->unsigned_str()) && (L->sign() != R->sign())) {
					return L->sign();
				}
			}
			return L->unsigned_str() < R->unsigned_str();
			break;
		}
		case NUM : {
			return  ((Number *)L)->lt(*((Number *)R));
			break;
		}
		case ADD : {
			Add * l = (Add * ) L;
			Add * r = (Add * ) R;
			for (const_iterator i = l->args_.begin(), j = r->args_.begin(); i != l->args_.end(); ++i, ++j) {
				if (auxLessThan(*i, *j)) {
				return true;
				}
				if (auxLessThan(*i, *j)) {
					return false;
				}
			}
			break;
		}
		case MUL : {
			Mul * l = (Mul * ) L;
			Mul * r = (Mul * ) R;
			for(const_iterator i = l->args_.begin(), j = r->args_.begin(); (i != l->args_.end()) && (j != r->args_.end()); ++i, ++j) {
				if (auxLessThan(*i, *j)) {
					return true;
				}
				if (auxLessThan(*j, *i)) {
					return false;
				}
			}
			break;
		}

		case POW : {
			Pow * l = (Pow * ) L;
			Pow * r = (Pow * ) R;
			if (auxLessThan(l->base_, r->base_)) {
				return true;
			}
			if (auxLessThan(r->base_, l->base_)) {
				return false;
			}
			if (auxLessThan(l->exponent_, r->exponent_)) {
				return true;
			}
			if (auxLessThan(r->exponent_, l->exponent_)) {
				return false;
			}
			break;
		}
		case FX : {
			Fx * l = (Fx * ) L;
			Fx * r = (Fx * ) R;
			if (auxLessThan(l->e_, r->e_)) {
				return true;
			}
			if (auxLessThan(r->e_, l->e_)) {
				return false;
			}
			if (l->name() != r->name()) {
				return l->name() < r->name();
			}
			if (l->sign() != r->sign()) {
				return l->sign();
			}
			break;
		}
		default : 
			return false;
	}
	return false;
}
}
